package indi.zjh.dp.orm;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.DocumentException;

import indi.zjh.dp.cache.ObjectCache;
import indi.zjh.dp.db.Configuration;
import indi.zjh.dp.db.DataSource;
import indi.zjh.dp.db.Session;
import indi.zjh.dp.db.TField;
import indi.zjh.dp.db.TableRepository;
import indi.zjh.dp.error.NoSuchObjectException;
import indi.zjh.dp.error.ObjectAlreadyContainedException;
import indi.zjh.dp.error.ObjectAttributeDuplicateException;
import indi.zjh.dp.error.UnsuportedFieldException;
import indi.zjh.dp.error.UpdateObjectFailedException;
import indi.zjh.dp.startup.Constant;
import indi.zjh.dp.type.GList;
import indi.zjh.dp.type.GSet;
import indi.zjh.dp.util.ClassUtil;
import indi.zjh.dp.util.CodecUtil;
import indi.zjh.dp.util.ReflectionUtil;
import indi.zjh.dp.util.StringUtil;


public class RelationClass implements BusinessStructListener{
	private static Logger logger = Logger.getLogger(RelationClass.class);
	// 该业务类对应的类模板
	protected Class<?> clazz;
	// 该业务类的属性, 不包含父类
	protected List<Field> fields;
	protected List<Field> basicFields;
	// 该业务类的属性, 包含所有父类
	protected List<Field> allFields;
	protected List<Field> allBasicFields;
	// 该业务类的属性hashmap, 便于按照属性名称查找, 数据表字段名称 -> 业务类字段对象
	protected Map<String, Field> allFieldsMap;
	// 数据表字段名称 -> 数据表字段对象
	protected Map<String, TField> allTFieldsMap;
	// 是否业务类
	protected boolean isBusinessClass;
	
	// 继承关系
	public RelationClass parentClass = null;
	public Set<RelationClass> subClass = null; // 所有的子类
	public Set<RelationClass> directSubClass = null; // 直接相邻的子类
	
	// 组合关系
	public Set<RelationClass> containingClass = null; // 包含的业务类集合
	public Set<RelationClass> directContainingClass = null; // 直接包含的业务类
	public Set<RelationClass> containedClass = null; // 该业务类被包含的业务类集合
	// field name -> field, 组合业务属性
	public Map<String, Field> containingField = null;
	
	// 聚合类型字段, 字段名称  -> 基本业务类型类名
	public Map<String, String> aggregationFieldMap = null;
	public Set<RelationClass> aggregationedClass = null; // 该业务类被聚合的业务类集合
	// 字段名称  -> field, 聚合属性字段
	public Map<String, Field> aggregationFieldMap2 = null;
	
	// 关联类型字段, 字段
	public Set<Field> associationFieldSet = null;
	public Set<RelationClass> associationClass = null;
	
	// 业务祖先类
	protected RelationClass rootClass = null;
	/**
	 * 业务模型类的构造函数, 初始化比较耗时, 有性能问题, 避免高频率调用, 尽量在一个业务版本里, 每个业务模型仅调用一次
	 * @param clazz
	 * @throws UnsuportedFieldException 
	 * @throws ClassNotFoundException 
	 */
	public RelationClass(Class<?> clazz) throws UnsuportedFieldException, ClassNotFoundException
	{
		this.clazz = clazz;
		this.fields = new ArrayList<Field>();
		this.basicFields = new ArrayList<Field>();
		this.aggregationFieldMap = new HashMap<String, String>();
		this.aggregationedClass = new HashSet<RelationClass>();
		this.aggregationFieldMap2 = new HashMap<String, Field>();
		this.associationFieldSet = new HashSet<Field>();
		this.directSubClass = new HashSet<RelationClass>();
		this.directContainingClass = new HashSet<RelationClass>();
		this.associationClass = new HashSet<RelationClass>();
		this.containingField = new HashMap<String, Field>();
		this.isBusinessClass = false;
		
		
		// 寻找最终父类, 判断是否继承业务基类
		Class<?> c = this.clazz;
		do
		{
			Class<?> parent = c.getSuperclass();
			if(parent.getName().equals("java.lang.Object"))
				break;
			else if(parent.getName().equals(Constant.BUSINESS_BASE_CLASS_NAME))
			{
				this.isBusinessClass = true;
				break;
			}
			c = parent;
		}while(true);
		
		// 忽略非业务类
		if(!this.isBusinessClass)
			return;
		
		// 寻找属性
		Field[] fields = this.clazz.getDeclaredFields();
		for(int i = 0; i < fields.length; ++i)
		{
			String typename = fields[i].getType().getName();
			Type t = fields[i].getGenericType();
			this.fields.add(fields[i]);
			fields[i].setAccessible(true);

			// 处理关联类
			if(typename.equals("indi.zjh.dp.orm.Relation"))
			{
				this.associationFieldSet.add(fields[i]);
			}
			// 处理聚合属性, 一个聚合类(List) 包含一个业务类
			else if (ParameterizedType.class.isAssignableFrom(t.getClass())) 
			{
				Type[] ts = ((ParameterizedType) t).getActualTypeArguments();
				if(ts.length != 1)
				{
					if(ts.length == 2 && (typename.equals("java.util.HashMap") || typename.equals("java.util.Hashtable")))
					{
						this.basicFields.add(fields[i]);
					}
					else 
					{
						throw new UnsuportedFieldException();
					}
					
				}
				else
				{
					if(typename.equals("indi.zjh.dp.type.GList") || typename.equals("indi.zjh.dp.type.GSet"))
					{
						String aggClassName = ts[0].getTypeName();
						if(ClassUtil.isExtended(aggClassName, Business.class.getName()))
						{
							this.aggregationFieldMap.put(fields[i].getName(), ts[0].getTypeName());
							this.aggregationFieldMap2.put(fields[i].getName(), fields[i]);
						}
					}
					else
					{
						throw new UnsuportedFieldException();
					}
				}
				
	        } 
			// 只添加基本类型, 而不包括业务类属性
			else if(!Business.class.isAssignableFrom(fields[i].getType()))
			{
				this.basicFields.add(fields[i]);
			}
		}
		
		// 寻找所有的属性, 包括所有父类属性
		this.allFields = new ArrayList<Field>();
		this.allBasicFields = new ArrayList<Field>();
		this.allFieldsMap = new HashMap<String, Field>();
		this.allTFieldsMap = new HashMap<String, TField>();
		
		Field[] flds = null;
		try {
			flds = ReflectionUtil.getAllFields(this.clazz);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		for(int i = 0; i < flds.length; ++i)
		{
			this.allFields.add(flds[i]);
			if(!Business.class.isAssignableFrom(flds[i].getType()))
			{
				this.allBasicFields.add(flds[i]);
			}
		}

	}
	
	public RelationClass getRootRelationClass()
	{
		return this.rootClass;
	}
	
	public Class<?> getClazz()
	{
		return this.clazz;
	}
	
	public String getName()
	{
		return this.clazz.getName();
	}
	
	public String getShortName()
	{
		String name = this.clazz.getName();
		String[] parts = name.split("\\.");
		return parts[parts.length - 1];
	}
	/**
	 * 获取该业务类的属性自动, 不包括继承和组合的
	 * @return
	 */
	public List<Field> getFields()
	{
		return this.fields;
	}
	
	/**
	 * 获取该业务类的基本属性字段, 不包括业务类型
	 * @return
	 */
	public List<Field> getBasicFields()
	{
		return this.basicFields;
	}
	
	/**
	 * 获取包括父类的指定字段
	 * @return
	 * @throws 
	 */
	public Field getFieldByName(String name)
	{

		if(this.allFieldsMap.containsKey(name))
			return this.allFieldsMap.get(name);
		
		return null;
	}
	
	public TField getTFieldByName(String name)
	{
		if(this.allTFieldsMap.containsKey(name))
			return this.allTFieldsMap.get(name);
		return null;
	}
	


	/**
	 * 在select之前填充字段映射表
	 * @param flds
	 * @throws ObjectAttributeDuplicateException
	 */
	public void addFieldMap(List<TField> flds) throws ObjectAttributeDuplicateException
	{
		// 只要更新一次
		if(this.allFieldsMap.size() > 0)
		{
			return;
		}
		Iterator<TField> iter = flds.iterator();
		while(iter.hasNext())
		{
			TField tf = iter.next();
			this.allFieldsMap.put(tf.name, tf.field);
			this.allTFieldsMap.put(tf.name, tf);
		}
	}
	
	/**
	 * 获取包括父类的所有字段
	 * @return
	 * @throws ClassNotFoundException 
	 */
	public List<Field> getAllFields()
	{
		return this.allFields;
	}
	
	public List<Field> getAllBasicFields()
	{
		return this.allBasicFields;
	}
	
	/**
	 * 获取包括父类的所有字段, 不包括指定的字段名
	 * @param excludeNames
	 * @return
	 */
	public List<Field> getAllFieldsExclude(String[] excludeNames)
	{
		List<Field> fields = new ArrayList<Field>();
		
		Iterator<Field> iter = this.allFields.iterator();
		while(iter.hasNext())
		{
			Field f = iter.next();
			boolean isFind = false;
			for(int j = 0; j < excludeNames.length; ++j)
			{
				if(excludeNames[j].equals(f.getName()))
					isFind = true;
			}
			if(!isFind)
				fields.add(f);
		}
		
		return fields;
	}
	
	public List<Field> getAllBasicFieldsExclude(String[] excludeNames)
	{
		List<Field> fields = new ArrayList<Field>();
		
		Iterator<Field> iter = this.allBasicFields.iterator();
		while(iter.hasNext())
		{
			Field f = iter.next();
			boolean isFind = false;
			for(int j = 0; j < excludeNames.length; ++j)
			{
				if(excludeNames[j].equals(f.getName()))
					isFind = true;
			}
			if(!isFind)
				fields.add(f);
		}
		
		return fields;
	}
	
	/**
	 * 计算一个业务类的指纹
	 * @return 指纹字符串
	 */
	public String computeSignature()
	{
		if(!this.isBusinessClass)
			return "";
		StringBuffer sBuffer = new StringBuffer();
		sBuffer.append(this.clazz.getName());
		Iterator<Field> iter = this.fields.iterator();
		while(iter.hasNext())
		{
			Field field = iter.next();
			sBuffer.append(field.getType().getName());
			sBuffer.append(field.getName());
		}
		return CodecUtil.sha1(sBuffer.toString());
	}
	
	/**
	 * 获取该业务类的相对文件路径
	 * @return 源代码项目文件路径
	 */
	public String getSrcFilePath()
	{
		String name = this.clazz.getName();
		return Constant.BUSINESS_SOURCE_DIRECTORY+"/"+name.replaceAll("\\.", "/")+".java";
	}
	
	/**
	 * 判断是否是业务类
	 * @return
	 */
	public boolean isBusiness()
	{
		return this.isBusinessClass;
	}
	
	
	/**
	 * 打印该业务类的父类关系链表
	 * @return
	 */
	public String getParentClassListInfo()
	{
		if(!this.isBusinessClass)
			return "";
		StringBuffer sb = new StringBuffer();
		
		RelationClass p = this;
		sb.append(this.getName());
		while((p = p.parentClass) != null)
		{
			sb.append(" <- " + p.getName());
		}
		return sb.toString();
	}
	
	public String getSubClassSetInfo()
	{
		if(this.subClass == null || this.subClass.size() == 0)
		{
			return this.getName() + " has no sub class";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " has "+this.subClass.size()+" sub classes ");
		
		String[] strs = new String[this.subClass.size()];
		int i = 0;
		Iterator<RelationClass> iter = this.subClass.iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getName();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	
	public String getDirectSubClassSetInfo()
	{
		if(this.directSubClass.size() == 0)
		{
			return this.getName() + " has no direct sub class";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " has "+this.directSubClass.size()+" direct sub classes ");
		
		String[] strs = new String[this.directSubClass.size()];
		int i = 0;
		Iterator<RelationClass> iter = this.directSubClass.iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getName();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	public String getDirectContainingClassSetInfo()
	{
		if(this.directContainingClass.size() == 0)
		{
			return this.getName() + " has no direct containing class";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " has "+this.directContainingClass.size()+" direct containing classes ");
		
		String[] strs = new String[this.directContainingClass.size()];
		int i = 0;
		Iterator<RelationClass> iter = this.directContainingClass.iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getName();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	public String getClassDesc()
	{
		StringBuffer sb = new StringBuffer();
		sb.append("class "+this.clazz.getName()+" { \n");
		Field[] fields = this.clazz.getDeclaredFields();
		//Business obj = null;
		Method method = null;
		Method toDictStringMethod = null;
		HashSet<String> getSetMethods = new HashSet<String>();
		try
		{
			//obj = (Business)this.clazz.newInstance();
			method = this.clazz.getMethod("toDictString", Field.class);
			toDictStringMethod = this.clazz.getMethod("toDictString", Method.class);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		try
		{
			for(int i = 0; i < fields.length; ++i)
			{
				if((fields[i].getModifiers() & Modifier.STATIC) == 0)
				{
					getSetMethods.add("get"+fields[i].getName().toLowerCase());
					getSetMethods.add("set"+fields[i].getName().toLowerCase());
					if(method == null)
						sb.append("\t"+" "+fields[i].getName()+"\n");
					else
						sb.append("\t"+" "+fields[i].getName()+" "+method.invoke(null, fields[i])+"\n");
				}
			}
		
			Method[] methods = this.clazz.getDeclaredMethods();
			for(int i = 0; i < methods.length; ++i)
			{
				if(methods[i].getName().equals("toString") || methods[i].getName().equals("toDictString"))
					continue;
				if(getSetMethods.contains(methods[i].getName().toLowerCase()))
					continue;
				if(toDictStringMethod == null)
				{
					sb.append("\t"+methods[i].getName()+"()\n");
				}
				else
				{
					sb.append("\t"+methods[i].getName()+"() "+toDictStringMethod.invoke(null, methods[i])+"\n");
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		sb.append("}\n");
		return sb.toString();
	}
	public String getAssociationClassSetInfo()
	{
		if(this.associationClass.size() == 0)
		{
			return this.getName() + " has no association class";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " has "+this.associationClass.size()+" association classes ");
		
		String[] strs = new String[this.associationClass.size()];
		int i = 0;
		Iterator<RelationClass> iter = this.associationClass.iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getName();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	
	public String getContainingFieldSetInfo()
	{
		if(this.containingField.size() == 0)
		{
			return this.getName() + " has no containing field";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " containing "+this.containingField.size() + " fields ");
		String[] strs = new String[this.containingField.size()];
		int i = 0;
		Iterator<Map.Entry<String, Field>> iter = this.containingField.entrySet().iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getKey();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	
	public String getContainingClassSetInfo()
	{
		if(this.containingClass == null)
		{
			return this.getName() + " has no containing class";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " containing "+this.containingClass.size() + " classes ");
		String[] strs = new String[this.containingClass.size()];
		int i = 0;
		Iterator<RelationClass> iter = this.containingClass.iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getName();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	
	public String getContainedClassSetInfo()
	{
		if(this.containedClass == null)
		{
			return this.getName() + " has no contained class";
		}
		StringBuffer sb = new StringBuffer();
		sb.append(this.getName() + " contained "+this.containedClass.size() + " classes ");
		String[] strs = new String[this.containedClass.size()];
		int i = 0;
		Iterator<RelationClass> iter = this.containedClass.iterator();
		while(iter.hasNext())
		{
			strs[i++] = iter.next().getName();
		}
		sb.append(String.join(", ", strs));
		return sb.toString();
	}
	

	
	/**
	 * 判断是否为指定业务类的父类
	 * @param son
	 * @return
	 */
	public boolean isParentOf(RelationClass son)
	{		
		return son.isSubOf(this);
	}
	
	/**
	 * 判断是否为指定类的子类
	 * @param father
	 * @return
	 */
	public boolean isSubOf(RelationClass father)
	{
		RelationClass p = this;
		do
		{
			p = p.parentClass;
			if(p == null)
				return false;
			if(p.getName().equals(father.getName()))
				return true;
		}while(true);
	}
	
	public boolean isContaining(RelationClass contain)
	{
		return this.containingClass.contains(contain);
	}

	public String toString()
	{
		StringBuffer buf = new StringBuffer();
		buf.append(this.clazz.getName()+"["+this.computeSignature()+"]\n");
		if(this.isBusinessClass)
			buf.append("\tBusi:true\n");
		else
			buf.append("\tBusi:false\n");
		
		if(!this.isBusinessClass)
			return buf.toString();

		if(this.fields.size() > 0)
		{
			Iterator<Field> iterf = this.fields.iterator();
			buf.append("\tFields:\n");
			while(iterf.hasNext())
			{
				Field f = iterf.next();
				buf.append("\t\t"+f.getType().getName()+" "+f.getName()+"\n");
			}
		}
			
		return buf.toString();
	}

	@Override
	public void extend(BusinessStructEvent event) throws Exception {
		// TODO Auto-generated method stub
		RelationClass subject = event.getSubject();
		//RelationClass object = event.getObject();
		
		// 当前是祖先到子孙的继承树
		if(event.getType() == BusinessStructEvent.FATHER_TO_SON)
		{
			if(this.isSubOf(subject))
			{
				// 修改子类的数据表为最终父类的数据表
				this.rootClass = subject;
			}
				
			
		}
		else if(event.getType() == BusinessStructEvent.SON_TO_FATHER)
		{
			
		}
	}

	@Override
	public void association(BusinessStructEvent event) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void composition(BusinessStructEvent event) throws Exception {
		// TODO Auto-generated method stub
//		RelationClass subject = event.getSubject();
//		RelationClass object = event.getObject();
//		
//		if(!this.getName().equals(subject.getName()))
//			return;
//		Table table = TableRepository.getInstance().getTable(subject);
//		
//		Iterator<Map.Entry<String, Field>> iter = subject.containingField.entrySet().iterator();
//		while(iter.hasNext())
//		{
//			Map.Entry<String, Field> entry = iter.next();
//			Field f = entry.getValue();
//			RelationClass typeclass = Configuration.getInstance().getVersion().getRelationClass(f.getType().getName());
//			Iterator<Field> niter = typeclass.getBasicFields().iterator();
//			while(niter.hasNext())
//			{
//				Field nf = niter.next();
//				//TField tf = BusinessMapDataStrategy.getDataField(nf, f);
//				//table.addField(tf);
//			}
//			
//		}
	
	}

	@Override
	public void aggregation(BusinessStructEvent event) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void dependency(BusinessStructEvent event) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void init(BusinessStructEvent event) throws Exception {
		// TODO Auto-generated method stub
		logger.debug("初始化业务模型类["+this.getName()+"]");

		// 静态初始化sql语句
		TableRepository rep = TableRepository.getInstance();
		Method m = TableRepository.class.getDeclaredMethod("loadClass", RelationClass.class);
		m.setAccessible(true);
		m.invoke(rep, this);
		
		//SerializeUtil.register(this.clazz);
		Iterator<Map.Entry<String, RelationClass>> iter = Configuration.getInstance().getVersion().getRelationClassIterator();
		Session sess = Configuration.getSessionFactory().createSession();
		sess.loadTables();
		while(iter.hasNext())
		{
			Map.Entry<String, RelationClass> entry = iter.next();
			// 自己不和自己发生关系
			//if(this.getName().equals(entry.getKey()))
			//	continue;
			
			
			RelationClass another = entry.getValue();
			
			// 判断是否和别的业务模型 有关系
			boolean isRelated = false;
			if(this.directSubClass.contains(another) || another.directSubClass.contains(this))
			{
				isRelated = true;
			}
			else
			{
				if(this.directContainingClass.contains(another) || another.directContainingClass.contains(this))
				{
					isRelated = true;
				}
				else
				{
					if(this.aggregationedClass.contains(another) || another.aggregationedClass.contains(this))
					{
						isRelated = true;
					}
					else
					{
						if(this.associationClass.contains(another) || another.associationClass.contains(this))
						{
							isRelated = true;
						}
					}
				}
			}
			
			if(isRelated == false)
			{
				continue;
			}
			String tableName = BusinessMapDataStrategy.getRelationTableName(this, another);
			if(!sess.isTableExist(tableName))
			{
				logger.info("创建关系表:"+tableName);
				String sql = BusinessMapDataStrategy.getRelationCreateSql(this, another);
				sess.alter(sql);
				String[] sqls = BusinessMapDataStrategy.getRelationTableIndexSql(this, another);
				for(int i = 0; i < sqls.length; ++i)
				{
					System.out.println("debug:"+sqls[i]);
					sess.alter(sqls[i]);
				}
			}
		}
		sess.close();
		
	}
	

	
}
